// app/api/data-room/[projectId]/upload/route.ts import { NextRequest, NextResponse } from 'next/server'; import { getServerSession } from 'next-auth/next'; import { authOptions } from '@/app/api/auth/[...nextauth]/route'; import { FileService, type FileAccessContext } from '@/lib/services/fileService'; import { saveDRMFile, saveFileStream } from '@/lib/file-stroage'; export async function POST( request: NextRequest, { params }: { params: { projectId: string } } ) { try { const session = await getServerSession(authOptions); if (!session?.user) { return NextResponse.json({ error: '인증이 필요합니다' }, { status: 401 }); } const context: FileAccessContext = { userId: Number(session.user.id), userDomain: session.user.domain || 'partners', userEmail: session.user.email, ipAddress: request.ip || request.headers.get('x-forwarded-for') || undefined, userAgent: request.headers.get('user-agent') || undefined, }; // 내부 사용자만 업로드 가능 if (session.user.domain === 'partners') { return NextResponse.json( { error: '파일 업로드 권한이 없습니다' }, { status: 403 } ); } const formData = await request.formData(); const file = formData.get('file') as File; const category = formData.get('category') as string; const parentId = formData.get('parentId') as string | null; const fileSize = formData.get('fileSize') as string | null; if (!file) { return NextResponse.json( { error: '파일이 제공되지 않았습니다' }, { status: 400 } ); } // 대용량 파일 임계값 (10MB) const LARGE_FILE_THRESHOLD = 10 * 1024 * 1024; const actualFileSize = fileSize ? parseInt(fileSize) : file.size; let result; // 파일 크기에 따라 다른 저장 방법 사용 if (actualFileSize > LARGE_FILE_THRESHOLD) { console.log(`🚀 대용량 파일 스트리밍 저장: ${file.name} (${(actualFileSize / 1024 / 1024).toFixed(2)}MB)`); // 대용량 파일은 스트리밍 저장 사용 result = await saveFileStream({ file, directory: `projects/${params.projectId}`, originalName: file.name, userId: session.user.id }); } else { console.log(`📦 일반 파일 저장: ${file.name} (${(actualFileSize / 1024 / 1024).toFixed(2)}MB)`); // 작은 파일은 기존 DRM 저장 방식 사용 result = await saveDRMFile( file, async (file) => file.arrayBuffer(), // 이미 복호화된 데이터 `projects/${params.projectId}`, session.user.id ); } if (!result.success) { return NextResponse.json( { error: result.error || '파일 저장에 실패했습니다' }, { status: 500 } ); } // DB에 파일 정보 저장 const fileService = new FileService(); const newFile = await fileService.createFileItem( { name: result.originalName || file.name, type: 'file', category: category as 'public' | 'restricted' | 'confidential' | 'internal', parentId, size: result.fileSize || actualFileSize, mimeType: file.type, filePath: result.publicPath, projectId: params.projectId, }, context ); return NextResponse.json({ ...newFile, uploadResult: { ...result, uploadMethod: actualFileSize > LARGE_FILE_THRESHOLD ? 'stream' : 'buffer', fileSizeMB: (actualFileSize / 1024 / 1024).toFixed(2) }, }, { status: 201 }); } catch (error) { console.error('파일 업로드 오류:', error); return NextResponse.json( { error: '파일 업로드에 실패했습니다', details: error instanceof Error ? error.message : undefined }, { status: 500 } ); } } // 업로드 진행률 확인 (선택적) export async function GET( request: NextRequest, { params }: { params: { projectId: string } } ) { try { const session = await getServerSession(authOptions); if (!session?.user) { return NextResponse.json({ error: '인증이 필요합니다' }, { status: 401 }); } // 업로드 상태 확인 로직 (필요시 구현) return NextResponse.json({ message: '업로드 상태 확인 엔드포인트' }); } catch (error) { return NextResponse.json( { error: '상태 확인 실패' }, { status: 500 } ); } }